/*
======================================================================
clip.c

Functions for LWO2 image references.

Ernie Wright  17 Sep 00
======================================================================
*/

#include <stdio.h>
#include <stdlib.h>
#include "lwo2.h"


/*
======================================================================
lwFreeClip()

Free memory used by an lwClip.
======================================================================
*/
void lwFreeClip(lwClip * clip)
{
	if(clip)
	{
		lwListFree(clip->ifilter, (void *)lwFreePlugin);
		lwListFree(clip->pfilter, (void *)lwFreePlugin);
		free(clip);
	}
}


/*
======================================================================
lwGetClip()

Read image references from a CLIP chunk in an LWO2 file.
======================================================================
*/
lwClip         *lwGetClip(FILE * fp, int cksize)
{
	lwClip         *clip;
	lwPlugin       *filt;
	unsigned int    id;
	unsigned short  sz;
	int             pos, rlen;


	/* allocate the Clip structure */
	clip = calloc(1, sizeof(lwClip));
	if(!clip)
		goto Fail;

	clip->contrast.val = 1.0f;
	clip->brightness.val = 1.0f;
	clip->saturation.val = 1.0f;
	clip->gamma.val = 1.0f;

	/* remember where we started */
	set_flen(0);
	pos = ftell(fp);

	/* index */
	clip->index = getI4(fp);

	/* first subchunk header */
	clip->type = getU4(fp);
	sz = getU2(fp);
	if(0 > get_flen())
		goto Fail;

	sz += sz & 1;
	set_flen(0);

	switch (clip->type)
	{
		case ID_STIL:
			clip->source.still.name = getS0(fp);
			break;

		case ID_ISEQ:
			clip->source.seq.digits = getU1(fp);
			clip->source.seq.flags = getU1(fp);
			clip->source.seq.offset = getI2(fp);
			getU2(fp);			/* Legacy Cruft: Nothing to see here */
			clip->source.seq.start = getI2(fp);
			clip->source.seq.end = getI2(fp);
			clip->source.seq.prefix = getS0(fp);
			clip->source.seq.suffix = getS0(fp);
			break;

		case ID_ANIM:
			clip->source.anim.name = getS0(fp);
			clip->source.anim.server = getS0(fp);
			rlen = get_flen();
			clip->source.anim.data = getbytes(fp, sz - rlen);
			break;

		case ID_XREF:
			clip->source.xref.index = getI4(fp);
			clip->source.xref.string = getS0(fp);
			break;

		case ID_STCC:
			clip->source.cycle.lo = getI2(fp);
			clip->source.cycle.hi = getI2(fp);
			clip->source.cycle.name = getS0(fp);
			break;

		default:
			break;
	}

	/* error while reading current subchunk? */
	rlen = get_flen();
	if(rlen < 0 || rlen > sz)
		goto Fail;

	/* skip unread parts of the current subchunk */
	if(rlen < sz)
		fseek(fp, sz - rlen, SEEK_CUR);

	/* end of the CLIP chunk? */
	rlen = ftell(fp) - pos;
	if(cksize < rlen)
		goto Fail;
	if(cksize == rlen)
		return clip;

	/* process subchunks as they're encountered */
	id = getU4(fp);
	sz = getU2(fp);
	if(0 > get_flen())
		goto Fail;

	while(1)
	{
		sz += sz & 1;
		set_flen(0);

		switch (id)
		{
			case ID_TIME:
				clip->start_time = getF4(fp);
				clip->duration = getF4(fp);
				clip->frame_rate = getF4(fp);
				break;

			case ID_CONT:
				clip->contrast.val = getF4(fp);
				clip->contrast.eindex = getVX(fp);
				break;

			case ID_BRIT:
				clip->brightness.val = getF4(fp);
				clip->brightness.eindex = getVX(fp);
				break;

			case ID_SATR:
				clip->saturation.val = getF4(fp);
				clip->saturation.eindex = getVX(fp);
				break;

			case ID_HUE:
				clip->hue.val = getF4(fp);
				clip->hue.eindex = getVX(fp);
				break;

			case ID_GAMM:
				clip->gamma.val = getF4(fp);
				clip->gamma.eindex = getVX(fp);
				break;

			case ID_NEGA:
				clip->negative = getU2(fp);
				break;

			case ID_IFLT:
			case ID_PFLT:
				filt = calloc(1, sizeof(lwPlugin));
				if(!filt)
					goto Fail;

				filt->name = getS0(fp);
				filt->flags = getU2(fp);
				rlen = get_flen();
				filt->data = getbytes(fp, sz - rlen);

				if(id == ID_IFLT)
				{
					lwListAdd((void *)&clip->ifilter, filt);
					clip->nifilters++;
				}
				else
				{
					lwListAdd((void *)&clip->pfilter, filt);
					clip->npfilters++;
				}
				break;

			default:
				break;
		}

		/* error while reading current subchunk? */
		rlen = get_flen();
		if(rlen < 0 || rlen > sz)
			goto Fail;

		/* skip unread parts of the current subchunk */
		if(rlen < sz)
			fseek(fp, sz - rlen, SEEK_CUR);

		/* end of the CLIP chunk? */
		rlen = ftell(fp) - pos;
		if(cksize < rlen)
			goto Fail;
		if(cksize == rlen)
			break;

		/* get the next chunk header */
		set_flen(0);
		id = getU4(fp);
		sz = getU2(fp);
		if(6 != get_flen())
			goto Fail;
	}

	return clip;

  Fail:
	lwFreeClip(clip);
	return NULL;
}


/*
======================================================================
lwFindClip()

Returns an lwClip pointer, given a clip index.
======================================================================
*/
lwClip         *lwFindClip(lwClip * list, int index)
{
	lwClip         *clip;

	clip = list;
	while(clip)
	{
		if(clip->index == index)
			break;
		clip = clip->next;
	}
	return clip;
}
